#include "parser.hpp"
#include "ast.hpp"

Parser::Parser(Lexer* lex) {
    _lex = lex;
    _cur_token = NULL;
}

Token* Parser::get_token() {
    if (_cur_token == NULL) {
        _cur_token = _lex->next();
    }
    
    return _cur_token;
}

void Parser::consume() {
    _cur_token = NULL;
}

void Parser::match(TokenType tt) {
    get_token();
    if (_cur_token->_tt != tt) {
        printf("Parser Error: expected %d, but got %d\n", tt, _cur_token->_tt);
        assert(_cur_token->_tt == tt); // to crash.
    }
    else {
        consume();
    }
}

Node* Parser::stmts() {
    ListNode* l = new ListNode();
    Token* t = get_token();
    while (t != NULL && t->_tt != T_RIGHT_BRACE) {
        l->add(stmt());
        t = get_token();
    }

    return l;
}

Node* Parser::stmt() {
    Token* t = get_token();
    if (t->_tt == T_KW_PRINT) {
        Node* n = print_stmt();
        t = get_token();
        match(T_ENDL);
        return n;
    }
    else if (t->_tt == T_KW_IF) {
        return if_stmt();
    }
    return NULL;
}

Node* Parser::if_stmt() {
    match(T_KW_IF);
    match(T_LEFT_PAR);
    Node* cond = or_test();
    match(T_RIGHT_PAR);
    Node* then_part = code_block();

    Token* t = get_token();
    Node* else_part = NULL;
    if (t != NULL && t->_tt == T_KW_ELSE) {
        match(T_KW_ELSE);
        else_part = code_block();
    }

    return new IfNode(cond, then_part, else_part);
}

Node* Parser::code_block() {
    match(T_LEFT_BRACE);
    Node* n = stmts();
    match(T_RIGHT_BRACE);
    return n;
}

CmpNode* Parser::cmp_op() {
    Token* t = get_token();
    CmpNode* n = NULL;

    if (t->_tt == T_LT) {
        match(T_LT);
        n = new CmpNode(CmpNode::CMP_LT);   
    }
    else if (t->_tt == T_EQU) {
        match(T_EQU);
        n = new CmpNode(CmpNode::CMP_EQUAL);
    }

    return n;
}

Node* Parser::condition() {
    Node* left = expression();
    CmpNode* cmp = cmp_op();
    Node* right = expression();

    cmp->set_left(left);
    cmp->set_right(right);
    return cmp;
}

Node* Parser::print_stmt() {
    match(T_KW_PRINT);
    match(T_LEFT_PAR);
    Node* n = new PrintNode(expression());
    match(T_RIGHT_PAR);

    return n;
}

/*
 * expression -> lambda_def
 *              | or_test
 *              | or_test IF or_test ELSE or_test
 */
Node* Parser::expression() {
    Node* a = or_test();
    return a;
}

/*
 * or_test -> and_test
 *          | or_test OR and_test
 */
Node* Parser::or_test() {
    Node* a = and_test();
    Token* op = get_token();
    while (op != NULL && op->_tt == TokenType::LOGIC_OR) {
        match(LOGIC_OR);
        Node* b = and_test();
        a = new BinaryOp(AST_OP_LOG_OR, a, b);
        op = get_token();
    }

    return a;
}

/*
 * and_test -> not_test
 *          | not_test "&&" and_test
 */
Node* Parser::and_test() {
    Node* a = not_test();
    Token* op = get_token();
    while (op != NULL && op->_tt == TokenType::LOGIC_AND) {
        consume();
        Node* b = and_test();
        a = new BinaryOp(AST_OP_LOG_AND, a, b);
        op = get_token();
    }

    return a;
}

/*
 * not_test -> "!" not_test
 *           | comparison
 */
Node* Parser::not_test() {
    Token* t = get_token();

    if (t->_tt == TokenType::LOGIC_NOT) {
        consume();
        return new LogicNotNode(not_test());
    }

    return comparison();
}

Node* Parser::comparison() {
    Node* a = or_expr();

    CmpNode* cmp = cmp_op();
    while (cmp != NULL) {
        Node* b = or_expr();
        cmp->set_left(a);
        cmp->set_right(b);
        a = cmp;
        cmp = cmp_op();
    }

    return a;
}

Node* Parser::or_expr() {
    Node* a = xor_expr();
    Token* op = get_token();
    while (op != NULL && op->_tt == TokenType::BIT_OR) {
        consume();
        Node* b = xor_expr();
        a = new BinaryOp(AST_OP_BIT_OR, a, b);
        op = get_token();
    }

    return a;
}

Node* Parser::xor_expr() {
    Node* a = and_expr();
    Token* op = get_token();
    while (op != NULL && op->_tt == TokenType::BIT_XOR) {
        consume();
        Node* b = and_expr();
        a = new BinaryOp(AST_OP_BIT_XOR, a, b);
        op = get_token();
    }

    return a;
}

Node* Parser::and_expr() {
    Node* a = shift_expr();
    Token* op = get_token();
    while (op != NULL && op->_tt == TokenType::BIT_AND) {
        consume();
        Node* b = shift_expr();
        a = new BinaryOp(AST_OP_BIT_AND, a, b);
        op = get_token();
    }

    return a;
}

Node* Parser::shift_expr() {
    Node* a = arith_expr();
    Token* op = get_token();
    while (op != NULL &&
            (op->_tt == TokenType::LEFT_SHIFT ||
             op->_tt == TokenType::RIGHT_SHIFT)) {
        TokenType op_tt = op->_tt;
        consume();
        Node* b = arith_expr();
        if (op_tt == TokenType::LEFT_SHIFT) {
            a = new BinaryOp(AST_OP_LEFT_SHIFT, a, b);
        }
        else {
            a = new BinaryOp(AST_OP_RIGHT_SHIFT, a, b);
        }
        op = get_token();
    }

    return a;
}

Node* Parser::arith_expr() {
    Node* a = term();
    Token* op = get_token();
    while (op != NULL &&
        (op->_tt == TokenType::T_PLUS || op->_tt == TokenType::T_MINUS)) {
        TokenType op_tt = op->_tt;
        consume();
        Node* b = term();
        if (op_tt == TokenType::T_PLUS) {
            a = new BinaryOp(AST_OP_ADD, a, b);
        } else {
            a = new BinaryOp(AST_OP_SUB, a, b);
        }

        op = get_token();
    }

    return a;
}

Node* Parser::term() {
    Node* a = factor();
    Token* op = get_token();
    while (op != NULL &&
        (op->_tt == T_MULT || op->_tt == T_DIV)) {
        consume();
        Node* b = factor();
        if (op->_tt == T_MULT) {
            a = new BinaryOp(AST_OP_MUL, a, b);
        } else {
            a = new BinaryOp(AST_OP_DIV, a, b);
        }
        
        op = get_token();
    }
    
    return a;
}

Node* Parser::factor() {
    Token* data = get_token();
    if (data->_tt == T_INT) {
        consume();
        return new ConstInt(stoi(data));
    }
    else if (data->_tt == T_MINUS) {
        consume();
        return new BinaryOp(AST_OP_SUB, new ConstInt(0), factor());
    }
    else if (data->_tt == T_LEFT_PAR) {
        match(T_LEFT_PAR);
        Node* a = expression();
        match(T_RIGHT_PAR);
        
        return a;
    }
    return NULL;
}
int Parser::stoi(Token* data) {
    int value = 0;
    for (int i = 0; i < data->_length; i++) {
        value = value * 10 + data->_value[i] - '0';
    }
    
    return value;
}

void Parser::eval() {
    stmts()->dump();
}

